/**
 * ex11. context
 *
 * @author cyy
 * @date 2020/12/24
 */
package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

/*
	context
		用于简化对单个请求的多个goroutine之间与请求域、取消信号、截止时间等相关操作，
		这些操作可能涉及多个API调用。
*/
// var exitChan = make(chan bool, 1)
var wg sync.WaitGroup

func f(ctx context.Context) {
	defer wg.Done()
LOOP:
	for {
		fmt.Println("1")
		time.Sleep(time.Millisecond * 500)
		select {
		// case <- exitChan:
		case <-ctx.Done():
			break LOOP
		default:
		}
	}
}

func main() {

	/*
		context.Context 是一个接口，定义了四个需要实现的方法：
			type Context interface {
				// Deadline() 需要返回当前 ctx 被取消的时间，也即是完成工作的截止时间（ddl）
				Deadline() (deadline time.Time, ok bool)

				// Done() 需要返回一个channel，其会在当前工作完成或者上下文被取消后关闭，多次调用只会返回同一个channel
				Done() <-chan struct{}

				// Err() 会返回当前 ctx 结束的原因，只会在 Done() 返回的channel被关闭时才会返回非空的值
				//		- 如果当前 ctx 被取消就会返回 Canceled 错误
				//		- 如果当前 ctx 超时就会返回 DeadlineExceeded 错误
				Err() error

				// Value() 会从 ctx 中返回key对应的value，对于同一个上下文来说，多次调用并传入相同的key会返回相同的结果，该方法仅用于传递跨api和进程间跟请求域的数据
				//		所提供的key必须是可比较的，且不为string类型或其他内置类型，以避免使用上下文在包之间发生冲突
				Value(key interface{}) interface{}
			}
		其中 Background() 和 T ODO() 分别是两个内置的实现

		With系列

	*/
	/*	ctx, cancel := context.WithCancel(context.Background())
		wg.Add(1)
		go f(ctx)
		time.Sleep(time.Second * 5)
		// exitChan <- true
		cancel()
		wg.Wait()
	*/
	d := time.Now().Add(200 * time.Millisecond)
	ctx, cancel := context.WithDeadline(context.Background(), d)
	// 尽管ctx会过期，但是如果不手动调用cancel()函数的话，该上下文及其父类只有在下一次垃圾回收时才会被回收
	defer cancel()

	select {
	case <-time.After(1 * time.Second):
		fmt.Println(1)
	case <-ctx.Done():
		fmt.Println(ctx.Err())
	}

}
